﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing.Drawing2D;
using System.Linq;
using System.Management;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;

namespace CMS.Util
{
    public static class Utils
    {
        #region HASH/加密相关
        /// <summary>
        /// 计算当前计算机的指纹
        /// </summary>
        public static class FingerPrint
        {
            /// <summary>
            /// 缓存已经计算过的指纹
            /// </summary>
            private static string _FingerPrint = null;

            /// <summary>
            /// 获取当前计算机的指纹
            /// </summary>
            /// <returns></returns>
            public static string Value()
            {
                if (_FingerPrint == null)
                {
                    var src = $"CPU:{CPUID()}\nBIOS:{BIOSID()}\nBASE:{BASEBOARDID()}\nDISK:{DISKID()}\nVIDEO:{VIDEOID()}\nMAC:{MACID()}";
                    Debug.WriteLine("Computer Hash src:"+src);

                    _FingerPrint = MD5(src);
                    Debug.WriteLine("Computer Hash:" + _FingerPrint);
                }
                return _FingerPrint;
            }

            #region Original Device ID Getting Code
            private static string Identifier(string wmiClass, string wmiProperty, string wmiMustBeTrue)
            {
                string result = "";
                ManagementClass mc = new ManagementClass(wmiClass);
                ManagementObjectCollection moc = mc.GetInstances();
                foreach (ManagementObject mo in moc)
                {
                    if (mo[wmiMustBeTrue].ToString() == "True")
                    {
                        //Only get the first one
                        if (result == "")
                        {
                            try
                            {
                                result = mo[wmiProperty].ToString();
                                break;
                            }
                            catch
                            {
                            }
                        }
                    }
                }
                return result;
            }
            //Return a hardware identifier
            private static string Identifier(string wmiClass, string wmiProperty)
            {
                string result = null;
                ManagementClass mc = new ManagementClass(wmiClass);
                ManagementObjectCollection moc = mc.GetInstances();
                foreach (ManagementObject mo in moc)
                {
                    //Only get the first one
                    if (result == null)
                    {
                        try
                        {
                            result = mo[wmiProperty]?.ToString();
                            break;
                        }
                        catch { }
                    }
                }
                return result ?? "";
            }
            private static string CPUID()
            {
                //Uses first CPU identifier available in order of preference
                //Don't get all identifiers, as it is very time consuming
                string retVal = Identifier("Win32_Processor", "UniqueId");
                if (retVal == "") //If no UniqueID, use ProcessorID
                {
                    retVal = Identifier("Win32_Processor", "ProcessorId");
                    if (retVal == "") //If no ProcessorId, use Name
                    {
                        retVal = Identifier("Win32_Processor", "Name");
                        if (retVal == "") //If no Name, use Manufacturer
                        {
                            retVal = Identifier("Win32_Processor", "Manufacturer");
                        }
                        //Add clock speed for extra security
                        retVal += Identifier("Win32_Processor", "MaxClockSpeed");
                    }
                }
                return retVal;
            }
            //BIOS Identifier
            private static string BIOSID()
            {
                return Identifier("Win32_BIOS", "Manufacturer")
                + Identifier("Win32_BIOS", "SMBIOSBIOSVersion")
                + Identifier("Win32_BIOS", "IdentificationCode")
                + Identifier("Win32_BIOS", "SerialNumber")
                + Identifier("Win32_BIOS", "ReleaseDate")
                + Identifier("Win32_BIOS", "Version");
            }
            //Main physical hard drive ID
            private static string DISKID()
            {
                return Identifier("Win32_DiskDrive", "Model")
                + Identifier("Win32_DiskDrive", "Manufacturer")
                + Identifier("Win32_DiskDrive", "Signature")
                + Identifier("Win32_DiskDrive", "TotalHeads");
            }
            //Motherboard ID
            private static string BASEBOARDID()
            {
                return Identifier("Win32_BaseBoard", "Model")
                + Identifier("Win32_BaseBoard", "Manufacturer")
                + Identifier("Win32_BaseBoard", "Name")
                + Identifier("Win32_BaseBoard", "SerialNumber");
            }
            //Primary video controller ID
            private static string VIDEOID()
            {
                return Identifier("Win32_VideoController", "DriverVersion")
                + Identifier("Win32_VideoController", "Name");
            }
            //First enabled network card ID
            private static string MACID()
            {
                return Identifier("Win32_NetworkAdapterConfiguration",
                    "MACAddress", "IPEnabled");
            }
            #endregion
        }

        /// <summary>
        /// 缓存MD5对象
        /// </summary>
        private static MD5 _MD5 = System.Security.Cryptography.MD5.Create();

        /// <summary>
        /// 缓存指纹的拆解数据
        /// </summary>
        private static int[] _FingerPrintCache = null;

        /// <summary>
        /// 计算MD5
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static string MD5(string data)
        {
            return string.Join("", from b in _MD5.ComputeHash(Encoding.UTF8.GetBytes(data)) select b.ToString("x2"));
        }

        /// <summary>
        /// 将一个字符串转换为Base64
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static string ToBase64(string data)
        {
            return Convert.ToBase64String(Encoding.UTF8.GetBytes(data));
        }

        /// <summary>
        /// 将一个Base64转为字符串
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public static string FromBase64(string data)
        {
            return Encoding.UTF8.GetString(Convert.FromBase64String(data));
        }

        /// <summary>
        /// 传入一个时间戳,生成识别计算机的Key
        /// </summary>
        /// <param name="time"></param>
        /// <returns></returns>
        public static string GetComputerHash(int time, string salt = "it's a salt")
        {
            string hash = MD5(FingerPrint.Value() + salt + time).Substring(24, 8);
            if (_FingerPrintCache == null)
            {
                _FingerPrintCache = (from c in FingerPrint.Value() select c >= 'a' ? c - 'a' + 10 : c - '0').ToArray();
            }
            int[] hashs = (from h in hash select h >= 'a' ? h - 'a' + 10 : h - '0').ToArray();
            int[] idXs = new int[32];
            for (int i = 0; i < 32; i++)
            {
                idXs[i] = (_FingerPrintCache[i] + hashs[i % 8]) % 16;
            }
            string idY = string.Join("", from c in idXs select c.ToString("x"));
            return idY + hash + time.ToString("x");
        }

        #endregion

        #region 时间相关
        /// <summary>
        /// 时间戳开始时间
        /// </summary>
        private static readonly DateTime _ZeroTime = new DateTime(1970, 1, 1, 0, 0, 0);

        /// <summary>
        /// 获取系统当前时间戳
        /// </summary>
        /// <returns></returns>
        public static int GetTimeStamp()
        {
            return (int)(DateTime.UtcNow - _ZeroTime).TotalSeconds;
        }

        /// <summary>
        /// 星期映射
        /// </summary>
        private static readonly string[] _WeekStr = new string[]
        {
            "星期日","星期一","星期二","星期三","星期四","星期五","星期六"
        };


        /// <summary>
        /// 将星期转换为对应的日期
        /// </summary>
        /// <param name="week"></param>
        /// <returns></returns>
        public static string DayOfWeekToStr(DayOfWeek week)
        {
            return _WeekStr[(int)week];
        }

        /// <summary>
        /// 将星期转为对应的1-7数字表示
        /// </summary>
        /// <param name="week"></param>
        /// <returns></returns>
        public static int DayOfWeekToInt(DayOfWeek week)
        {
            return week == DayOfWeek.Sunday ? 7 : (int)week;
        }

        /// <summary>
        /// 计算开始时间,返回的值是从0开始编号的
        /// </summary>
        /// <param name="flag"></param>
        /// <returns></returns>
        public static int CalcTimeStart(uint flag)
        {
            flag &= ~flag + 1;
            return (int)Math.Log(flag, 2);
        }

        /// <summary>
        /// 计算连续的BitFlag个数,用于统计课程的时长
        /// </summary>
        /// <param name="flag"></param>
        /// <returns></returns>
        public static int CalcTimeSpan(uint flag, int max)
        {
            flag &= (1u << max) - 1;
            int count = 0;
            while (flag != 0)
            {
                flag &= flag - 1;
                count++;
            }
            return count;
            //uint tmp = flag >> CalcTimeStart(flag);
            //for (int i = 0; i < 31; i++)
            //{
            //    if (tmp == (1 << i) - 1)
            //    {
            //        return i;
            //    }
            //}
            //throw new IndexOutOfRangeException("节次值不合法,可能是不连续或超出范围");
        }
        #endregion

        #region 拓展函数
        /// <summary>
        /// 使用指定的条件
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="ts"></param>
        /// <param name="predicate"></param>
        /// <returns></returns>
        public static int IndexOf<T>(this IEnumerable<T> _this, Predicate<T> predicate)
        {
            int i = 0;
            foreach (T t in _this)
            {
                if (predicate(t))
                {
                    return i;
                }
                i++;
            }
            return -1;
        }

        #endregion

        #region 图形相关函数
        /// <summary>
        /// 绘制一个圆角矩形
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="w"></param>
        /// <param name="h"></param>
        /// <param name="r"></param>
        /// <returns></returns>
        public static GraphicsPath GetRoundRect(int x, int y, int w, int h, int r)
        {
            GraphicsPath path = new GraphicsPath();

            int r2 = 2 * r;
            path.AddLine(x + r, y, x + w - r, y);
            path.AddLine(x + w, y + r, x + w, y + h - r);
            path.AddLine(x + w - r, y + h, x + r, y + h);
            path.AddLine(x, y + h - r, x, y + r);
            path.CloseFigure();
            return path;
        }
        #endregion

        #region 位标记处理相关
        /// <summary>
        /// 分解位标记(将位标记转换为对应的数字序列)
        /// </summary>
        /// <param name="flag"></param>
        /// <returns>位标记的第一位将返回0</returns>
        public static IEnumerable<int> SplitBitmapFlag(uint flag)
        {
            while (flag != 0)
            {
                yield return (int)Math.Log(flag & -flag, 2);
                flag &= flag - 1;
            }
        }

        /// <summary>
        /// 计算位标记中1的个数
        /// </summary>
        /// <returns></returns>
        public static int CalcBitmapNonZeroCount(uint flag)
        {
            int i = 0;
            while (flag != 0)
            {
                flag &= flag - 1;
                i++;
            }
            return i;
        }

        /// <summary>
        /// 计算当前周次并转为对应的位标记
        /// </summary>
        /// <param name="date"></param>
        /// <param name="startDate"></param>
        /// <returns></returns>
        public static int CalcClassWeek(DateTime date, DateTime startDate)
        {
            return 1 << ((date - startDate).Days / 7);
        }

        #region 数据转换相关
        /// <summary>
        /// 鉴定数据的成分
        /// </summary>
        private static readonly Regex _Regex0 = new Regex(@"[0-9\-,单双]+");

        /// <summary>
        /// 测试是否为逗号分隔的字符串
        /// </summary>
        private static readonly Regex _Regex1 = new Regex(@"(?:\d+,?)+");

        /// <summary>
        /// 测试是否为负号表示的区间值
        /// </summary>
        private static readonly Regex _Regex2 = new Regex(@"(\d+)-(\d+)([单双])?|(\d{2})\d{2}(\d{2})|(\d{1,2})");

        /// <summary>
        /// 判断区间的表达式是否正确的正则表达式
        /// </summary>
        private static readonly Regex _Regex3 = new Regex(@"^(?:\d+-\d+[单双]?[,]?|\d{2}\d{2}\d{2}[,]?|\d{1,2}[,]?)+$");

        /// <summary>
        /// 将一个字符串区间值转换为对应的位标记
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static uint ConvertValue(string value)
        {
            if (value == null)
            {
                throw new ArgumentNullException("value");
            }

            if (!_Regex0.Match(value).Success)
            {
                throw new InvalidCastException("转换的值中存在无法识别的字符");
            }
            uint ret = 0;
            if (_Regex1.Match(value).Success)
            {
                string[] tmp1 = value.Split(',');
                foreach (string tmp2 in tmp1)
                {
                    Match match = _Regex2.Match(tmp2);
                    if (match.Success)
                    {
                        if (match.Groups[6].Success)
                        {
                            ret |= 1u << (int.Parse(match.Groups[6].Value) - 1);
                        }
                        else if (match.Groups[1].Success)
                        {
                            int i = int.Parse(match.Groups[1].Value);
                            int j = int.Parse(match.Groups[2].Value);
                            if (match.Groups[3].Success)
                            {
                                for (int k = match.Groups[3].Value == "单" == (i % 2 == 1) ? i : i + 1; k < j; k += 2)
                                {
                                    ret |= 1u << (k - 1);
                                }
                            }
                            else
                            {
                                ret |= (uint)((1 << (j - i + 1)) - 1) << (i - 1);
                            }
                        }
                        else if (match.Groups[4].Success)
                        {
                            int i = int.Parse(match.Groups[4].Value);
                            int j = int.Parse(match.Groups[5].Value);
                            ret = (uint)((1 << (j - i + 1)) - 1) << (i - 1);
                        }
                    }
                    else
                    {
                        throw new InvalidCastException("无法识别的格式:" + tmp2);
                    }
                }
            }
            return ret;
        }

        /// <summary>
        /// 将一个位标记转为对应的字符串区间
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static string ConvertValue(uint value)
        {
            if (value == 0)
            {
                return "";
            }

            int i = 1;
            int min = 0;
            StringBuilder sb = new StringBuilder();
            while (value > 0)
            {
                if ((value & 1) == 1)
                {
                    if (min == 0)
                    {
                        min = i;
                    }
                }
                else
                {
                    if (min != 0)
                    {
                        if (min == i - 1)
                        {
                            sb.Append(min).Append(',');
                        }
                        else
                        {
                            sb.Append($"{min}-{i - 1},");
                        }
                        min = 0;
                    }
                }
                i++;
                value >>= 1;
            }
            if (min != 0)
            {
                if (min == i - 1)
                {
                    sb.Append(min).Append(',');
                }
                else
                {
                    sb.Append($"{min}-{i - 1},");
                }
                min = 0;
            }
            return sb.ToString(0, sb.Length - 1);
        }

        /// <summary>
        /// 验证时间字符串
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static bool ValidateTime(string value)
        {
            bool ret = _Regex3.IsMatch(value);
            return ret;
        }
        #endregion


        #endregion

        #region 数据对象相关
        public static string[] GetProps<T>(T[] items, string name)
        {
            MethodInfo method = typeof(T).GetProperty(name).GetMethod;
            return (from i in items select method.Invoke(i, new object[0]).ToString()).ToArray();
        }
        #endregion

        #region 配置文件相关
        /// <summary>
        /// 全局配置文件
        /// </summary>
        public static PrivateProfile PrivateProfile { get; } = new PrivateProfile("config.ini");

        #endregion

        #region 网络相关

        /// <summary>
        /// UrlEncode编码函数
        /// </summary>
        /// <param name="str">待加密的Url部分</param>
        /// <returns></returns>
        public static string UrlEncode(string str)
        {
            StringBuilder sb = new StringBuilder();
            foreach (char s in str)
            {
                if (char.IsLetterOrDigit(s))
                {
                    sb.Append(s);
                }
                else if (s == ' ')
                {
                    sb.Append('+');
                }
                else
                {
                    foreach (byte i in Encoding.UTF8.GetBytes(s.ToString()))
                    {
                        sb.Append("%");
                        sb.Append(((int)s).ToString("X2"));
                    }
                }
            }
            return sb.ToString();
        }
        #endregion

        #region Excel 相关

        /// <summary>
        /// 计算电子表格宽度
        /// </summary>
        /// <param name="width"></param>
        /// <returns></returns>
        public static int GetSheetWidth(int width)
        {
            return (int)((width + 0.72) * 256);
        }

        #endregion
    }
}
